#include <MyFrameListener.h>

MyFrameListener::MyFrameListener(Ogre::RenderWindow* win, 
                                 Ogre::Camera* cam,
                                 Ogre::OverlayManager* om,
                                 Ogre::SceneManager* sceneManager)
{
  OIS::ParamList param;
  unsigned int windowHandle;
  std::ostringstream wHandleStr;
  std::fstream fs;
  int n;

  _camera = cam;
  _overlayManager = om;
  _win = win;
  _selectedNode = NULL;
  _mousePushed = false;
  _state = Menu;
  

  // Inicializar variables del juego
  srand((unsigned)time(NULL));
  _sec = new std::vector<int>;
  _gameState = 0;
  _colorStr = new Ogre::String;
  _puntos = 0;
  
  // Cargar lista de records
  _records = new std::list<int>;
  
  fs.open("records", ios::in);
  if (not fs.fail())
    while (fs.good()) {
      fs >> n;
      _records->push_back(n);
    }
  fs.close();

  _cameraNode = sceneManager->createSceneNode("cameraNode");
  sceneManager->getRootSceneNode()->addChild(_cameraNode);
  _cameraNode->setPosition(Ogre::Vector3(5, 20, 20));
  
  win->getCustomAttribute("WINDOW", &windowHandle);
  wHandleStr << windowHandle;
  param.insert(std::make_pair("WINDOW", wHandleStr.str()));
  
  _inputManager = OIS::InputManager::createInputSystem(param);
  _keyboard = static_cast<OIS::Keyboard*>
    (_inputManager->createInputObject(OIS::OISKeyboard, false));
  _mouse = static_cast<OIS::Mouse*>
    (_inputManager->createInputObject(OIS::OISMouse, false));

  _mouse->getMouseState().width = win->getWidth();
  _mouse->getMouseState().height = win->getHeight();

  _sceneManager = sceneManager;
  _raySceneQuery = _sceneManager->createRayQuery(Ogre::Ray());
}

MyFrameListener::~MyFrameListener()
{
  delete _sec;
  delete _colorStr;
  delete _records;
  _inputManager->destroyInputObject(_keyboard);
  _inputManager->destroyInputObject(_mouse);
  OIS::InputManager::destroyInputSystem(_inputManager);
}

Ogre::Ray MyFrameListener::setRayQuery(int posx, int posy, Ogre::uint32 mask)
{
  Ogre::Ray rayMouse = _camera->getCameraToViewportRay(
    posx/float(_win->getWidth()), posy/float(_win->getHeight()));

  _raySceneQuery->setRay(rayMouse);
  _raySceneQuery->setSortByDistance(true);
  _raySceneQuery->setQueryMask(mask);

  return rayMouse;
}

bool MyFrameListener::frameStarted(const Ogre::FrameEvent& evt)
{
  switch(_state)
  {
    case Menu: return menu(evt); break;
    case Game: return juego(evt); break;
    case Record: return records(evt); break;
    case Extra: return extras(evt); break;
    default: return false;
  };
}

bool MyFrameListener::menu(const Ogre::FrameEvent& evt)
{

  // Detección del ratón
  _mouse->capture();
  unsigned int posx = _mouse->getMouseState().X.abs;
  unsigned int posy = _mouse->getMouseState().Y.abs;
  Ogre::OverlayElement* mouseOverlay = _overlayManager->getOverlayElement("cursor");
  mouseOverlay->setLeft(posx);
  mouseOverlay->setTop(posy);

  bool mbleft = _mouse->getMouseState().buttonDown(OIS::MB_Left);

  if (mbleft and not _mousePushed)
  {
    _mousePushed = true;

    // Pulsar Botón jugar
    if ((posx > (319 * _win->getWidth() / 1280)) &&
        (posx < (613 * _win->getWidth() / 1280)) &&
        (posy > (196 * _win->getHeight() / 800)) &&
        (posy < (378 * _win->getHeight() / 800)))
    {
      _overlayManager->getOverlayElement("menuPlayImage")->show();
    }

    // Pulsar Botón records
    if ((posx > (657 * _win->getWidth() / 1280)) &&
        (posx < (995 * _win->getWidth() / 1280)) &&
        (posy > (198 * _win->getHeight() / 800)) &&
        (posy < (379 * _win->getHeight() / 800)))
    {
      _overlayManager->getOverlayElement("menuRecordsImage")->show();
    }

    // Pulsar Botón extras
    if ((posx > (319 * _win->getWidth() / 1280)) &&
        (posx < (621 * _win->getWidth() / 1280)) &&
        (posy > (417 * _win->getHeight() / 800)) &&
        (posy < (598 * _win->getHeight() / 800)))
    {
      _overlayManager->getOverlayElement("menuExtrasImage")->show();
    }

    // Pulsar Botón salir
    if ((posx > (657 * _win->getWidth() / 1280)) &&
        (posx < (957 * _win->getWidth() / 1280)) &&
        (posy > (417 * _win->getHeight() / 800)) &&
        (posy < (598 * _win->getHeight() / 800)))
    {
      _overlayManager->getOverlayElement("menuQuitImage")->show();
    }

  }

  if (not mbleft and _mousePushed)
  {	
    // Hay que sacar los hide() fuera de los if para que si soltamos
    // fuera del botón no se quede marcado
    _overlayManager->getOverlayElement("menuExtrasImage")->hide();
    _overlayManager->getOverlayElement("menuPlayImage")->hide();
    _overlayManager->getOverlayElement("menuRecordsImage")->hide();
    _overlayManager->getOverlayElement("menuQuitImage")->hide();
  
    // Soltar Botón jugar
    if ((posx > (319 * _win->getWidth() / 1280)) &&
      	(posx < (613 * _win->getWidth() / 1280)) &&
	    (posy > (196 * _win->getHeight() / 800)) &&
	    (posy < (378 * _win->getHeight() / 800)))
     {
       _overlayManager->getOverlayElement("menuImage")->hide(); 
       _state=Game;
     }
  	
    // Soltar Boton Salir
    if ((posx > (657 * _win->getWidth() / 1280)) &&
       	(posx < (957 * _win->getWidth() / 1280)) &&
       	(posy > (417 * _win->getHeight() / 800)) &&
       	(posy < (598 * _win->getHeight() / 800)))
     {
       return false;
     }
    	
    // Soltar Botón records
    if ((posx > (657 * _win->getWidth() / 1280)) &&
        (posx < (995 * _win->getWidth() / 1280)) &&
        (posy > (198 * _win->getHeight() / 800)) &&
        (posy < (379 * _win->getHeight() / 800)))
    {
	    _state = Record;
    }

    // Soltar Botón extras
    if ((posx > (319 * _win->getWidth() / 1280)) &&
        (posx < (621 * _win->getWidth() / 1280)) &&
        (posy > (417 * _win->getHeight() / 800)) &&
        (posy < (598 * _win->getHeight() / 800)))
     {
       _state = Extra;
     }
    	
    _mousePushed = false;
  }

  return true;
}

bool MyFrameListener::juego(const Ogre::FrameEvent &evt)
{
  OverlayElement *oe;
  Real deltaT = evt.timeSinceLastFrame; // Tiempo desde ultimo frame dibujado
  int posx, posy; // Posición del ratón
  bool mbleft; // Botones del ratón

  _keyboard->capture();
  _mouse->capture();

  // Posición del ratón
  posx = _mouse->getMouseState().X.abs;
  posy = _mouse->getMouseState().Y.abs;
  mbleft = _mouse->getMouseState().buttonDown(OIS::MB_Left);
  oe = _overlayManager->getOverlayElement("cursor");  
  oe->setLeft(posx);  oe->setTop(posy);  

  // Comprobar pulsacion teclas
  if(_keyboard->isKeyDown(OIS::KC_ESCAPE))
  {
    guardarRecords();
    reiniciarJuego();    
    _state = Menu;
    _overlayManager->getOverlayElement("cursor")->show();
    _overlayManager->getOverlayElement("menuImage")->show();
    _overlayManager->getByName("Info")->hide();
    return true;
  }
  
  _overlayManager->getByName("Info")->show();
  //_overlayManager->getOverlayElement("puntuacion")->show();
  _overlayManager->getOverlayElement("puntos")->setCaption(StringConverter::toString(_puntos));
      
  // Automata del juego
  switch (_gameState)
  {
    // Generar un nuevo color en la secuencia
    case 0:
      _overlayManager->getOverlayElement("secuencia")->show();
      _overlayManager->getOverlayElement("jugar")->hide();
      _overlayManager->getOverlayElement("pierde")->hide();
      _overlayManager->getOverlayElement("cursor")->hide();

    _sec->push_back(rand()%4);
    
    _n = 0;
    _timer = TIMEOFF;
    
    _gameState = 1;
    break;
  
    // Esperar TIMEOFF segundos con el boton apagado
    case 1:
      if ((_timer -= deltaT) <= 0) _gameState = 2;
      break;
    
    // Iluminar el boton correspondiente de la secuencia 
    case 2:
      _color = _sec->at(_n);
      
      if (_color == ROJO)       *_colorStr = "Rojo";
      else if (_color == VERDE) *_colorStr = "Verde";
      else if (_color == AZUL)  *_colorStr = "Azul";
      else                      *_colorStr = "Amarillo";

      _colorEnt = static_cast<Entity *>(
        _sceneManager->getSceneNode(*_colorStr)->getAttachedObject(0));
      _colorEnt->setMaterialName(*_colorStr + "Brillante");
      
      _timer = TIMEON;
      _gameState = 3;
      break;
    
    // Esperar TIMEON segundos con el boton encendido
    case 3:
      if ((_timer -= deltaT) <= 0) {
      
        _colorEnt->setMaterialName(*_colorStr); // Apagar boton
        _n++; // Avanzar al siguiente color de la secuencia
        
        // Aun quedan colores en la secuencia
        if (_n < static_cast<int>(_sec->size())) {
          _timer = TIMEOFF;
          _gameState = 1;
        }
        
        // No quedan colores en la secuencia
        else {
          _overlayManager->getOverlayElement("cursor")->show();
          _n = 0;
          _gameState = 4;
        }
      }
      break;

    // Iluminar boton seleccionado con el raton
    case 4:
      _overlayManager->getOverlayElement("secuencia")->hide();
      _overlayManager->getOverlayElement("jugar")->show();
      if (mbleft) {
        setRayQuery(posx, posy, BOTON);
        RaySceneQueryResult &result = _raySceneQuery->execute();
        RaySceneQueryResult::iterator it;
        _mousePushed=true;
      
        it = result.begin();
        
        if (it != result.end()) {
          _selectedNode = it->movable->getParentSceneNode();
          *_colorStr = _selectedNode->getName();

          if (*_colorStr == "Rojo")       _color = ROJO;
          else if (*_colorStr == "Verde") _color = VERDE;
          else if (*_colorStr == "Azul")  _color = AZUL;
          else if (*_colorStr == "Amarillo") _color = AMARILLO;
          else break;

          _colorEnt = static_cast<Entity *>(_selectedNode->getAttachedObject(0));
          _colorEnt->setMaterialName(*_colorStr + "Brillante");

          _gameState = 5;
        }
      }
      break;
    
    // Apagar boton seleccionado al soltar el raton
    case 5:
      if (not mbleft and _mousePushed) {
        _colorEnt->setMaterialName(*_colorStr);
        _mousePushed = false;
        _gameState = 6;
      }
      break;
    
    // Comparar seleccion con secuencia
    case 6:
      // Color equivocado
      if (_color != _sec->at(_n)){_gameState = 7; _timer=2;}
      
      // Color acertado
      else {
        _n++; // Avanzar de color en la secuencia
        _puntos+=10*(_n+1);
        
        // Aun no se completo la secuencia, volver a escoger
        if (_n < static_cast<int>(_sec->size())) _gameState = 4;
        
        // Se completo la secuencia, generar nuevo color
        else _gameState = 0;
      }
      break;
    
    // Al perder reiniciar el juego y mostrar menu
    case 7:
      _overlayManager->getOverlayElement("pierde")->show();
      
      if(mbleft) _mousePushed=true;

      if ((_timer -= deltaT) <= 0 && !mbleft && _mousePushed)
      {
        _mousePushed=false;
        guardarRecords();
        reiniciarJuego();
        _state = Menu;
        _overlayManager->getOverlayElement("menuImage")->show();
        _overlayManager->getOverlayElement("cursor")->show();
        _overlayManager->getByName("Info")->hide();
      }

      break;

  } // fin del autómata del juego

  return true;
}

void MyFrameListener::reiniciarJuego() {
  _sec->clear();  // Vaciar secuencia
  _gameState = 0; // Reiniciar estado del juego
  _puntos = 0;
  
  // Apagar botones
  _colorEnt = static_cast<Entity *>(
    _sceneManager->getSceneNode("Rojo")->getAttachedObject(0));
  _colorEnt->setMaterialName("Rojo");
  _colorEnt = static_cast<Entity *>(
    _sceneManager->getSceneNode("Verde")->getAttachedObject(0));
  _colorEnt->setMaterialName("Verde");
  _colorEnt = static_cast<Entity *>(
    _sceneManager->getSceneNode("Azul")->getAttachedObject(0));
  _colorEnt->setMaterialName("Azul");
  _colorEnt = static_cast<Entity *>(
    _sceneManager->getSceneNode("Amarillo")->getAttachedObject(0));
  _colorEnt->setMaterialName("Amarillo");
}

void MyFrameListener::guardarRecords()
{
  fstream fs;
  std::list<int>::iterator it;

  if (_puntos == 0) return;
  
  _records->push_back(_puntos);
  
  _records->sort();
  _records->reverse();
  
  while (_records->size() > 10)
    _records->pop_back();
  
  fs.open("records", ios::out | ios::trunc);
  
  for (it = _records->begin(); it != _records->end(); it++)
    fs << " " << *it;
 
  fs.close(); 
}

bool MyFrameListener::records(const Ogre::FrameEvent& evt)
{
  OverlayElement *oe;
  int posx, posy; // Posición del ratón
  bool mbleft;    // Botones del ratón
  std::stringstream recordsStr;
  _keyboard->capture();
  _mouse->capture();

  _overlayManager->getOverlayElement("Records")->show(); 

  // Posicion del raton
  posx = _mouse->getMouseState().X.abs;
  posy = _mouse->getMouseState().Y.abs;
  mbleft = _mouse->getMouseState().buttonDown(OIS::MB_Left); 
  oe = _overlayManager->getOverlayElement("cursor");  
  oe->setLeft(posx);  oe->setTop(posy);

  oe = _overlayManager->getOverlayElement("records");
  oe->setLeft(330 * _win->getWidth() / 1280);
  oe->setTop(260 * _win->getHeight() / 800);
  
  std::list<int>::iterator it;
  for (it = _records->begin(); it != _records->end(); it++)
    recordsStr << *it << "\n";
  
  oe->setCaption(recordsStr.str());

  if(mbleft) _mousePushed = true;

  if (_keyboard->isKeyDown(OIS::KC_ESCAPE)|| (!mbleft && _mousePushed)){
    _state=Menu;
    _overlayManager->getOverlayElement("Records")->hide();
    _mousePushed=false;
  }

  return true;
}
	
bool MyFrameListener::extras(const Ogre::FrameEvent& evt)
{
  OverlayElement *oe;
  int posx, posy;     // Posición del ratón
  bool mbleft;        // Botones del ratón
  	
  _keyboard->capture();
  _mouse->capture();

  _overlayManager->getOverlayElement("Extras")->show(); 

  // Posición del ratón
  posx = _mouse->getMouseState().X.abs;
  posy = _mouse->getMouseState().Y.abs;
  mbleft = _mouse->getMouseState().buttonDown(OIS::MB_Left); 
  oe = _overlayManager->getOverlayElement("cursor");  
  oe->setLeft(posx);  oe->setTop(posy);
	
  /*
  oe = _overlayManager->getOverlayElement("extras");
  oe->setLeft(330 * _win->getWidth() / 1280);
  oe->setTop(260 * _win->getHeight() / 800);
  oe->setCaption("Autores:\n\nLuis Miguel Garcia\nDario Martin\nEduardo Monroy");
  */
	
  if(mbleft) _mousePushed = true;

  if (_keyboard->isKeyDown(OIS::KC_ESCAPE)|| (!mbleft && _mousePushed)){
    _overlayManager->getOverlayElement("Extras")->hide();
    _state=Menu;
    _mousePushed=false;
  }

  return true;
}
